package com.simpleplayer.contentview.gles;

import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;

// 用于全景视频的渲染器.
public class SphereRenderer extends GLESRenderer {

    private static final String VERTEX_SHADER_CODE =
            "uniform mat4 uMVPMatrix;" +
                    "uniform mat4 uTextureMatrix;" +
                    "attribute vec4 aPosition;" +
                    "attribute vec4 aTextureCoord;" +
                    "varying vec2 vTextureCoord;" +
                    "void main() {" +
                    "    gl_Position = uMVPMatrix * aPosition;" +
                    "    vTextureCoord = (uTextureMatrix * aTextureCoord).xy;" +
                    "    vTextureCoord = (aTextureCoord).xy;" +
                    "}";

    private static final String FRAMENT_SHADER_CODE =
            "#extension GL_OES_EGL_image_external : require\n" +
                    "precision mediump float;" +
                    "varying vec2 vTextureCoord;" +
                    "uniform samplerExternalOES sTexture;" +
                    "void main() {" +
                    "    vec4 color = texture2D(sTexture, vTextureCoord);" +
                    "    gl_FragColor = color;" +
                    "}";

    private static final int SPHERE_SLICES = 180;
    private static final int SPHERE_INDICES_PER_VERTEX = 1;
    private static final float SPHERE_RADIUS = 500.0f;

    private ShaderProgram mShaderProgram;

    private final float[] mPvMatrix = new float[16];
    private final float[] mMvpMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    private final float[] mModelMatrix = new float[16];
    private final float[] mCamera = new float[3];

    private final Sphere sphere;

    private static final float FOVY = 90f;
    private static final float Z_NEAR = 0.1f;
    private static final float Z_FAR = 1000f;
    private static final float DRAG_FRICTION = 0.1f;

    private float lon = 90;
    private float lat = 0;

    private boolean mPendingCameraUpdate = false;
    private boolean mInited = false;

    public SphereRenderer() {
        sphere = new Sphere(SPHERE_SLICES, 0.f, 0.f, 0.f,
                SPHERE_RADIUS, SPHERE_INDICES_PER_VERTEX);
    }

    @Override
    public void initGLProgram() {
        mShaderProgram = new ShaderProgram(VERTEX_SHADER_CODE, FRAMENT_SHADER_CODE);
        mInited = true;
    }

    @Override
    public boolean isInited() {
        return mInited;
    }

    @Override
    public void drawFrame(int textureId, float[] textureMatrix) {

        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        GLES20.glDisable(GLES20.GL_DEPTH_TEST);

        GLES20.glUseProgram(mShaderProgram.getShaderHandle());

        int aPositionLocation = mShaderProgram.getAttribute("aPosition");
        int uMVPMatrixLocation = mShaderProgram.getUniform("uMVPMatrix");
        int uTextureMatrixLocation = mShaderProgram.getUniform("uTextureMatrix");
        int aTextureCoordLocation = mShaderProgram.getAttribute("aTextureCoord");

        GLES20.glEnableVertexAttribArray(aPositionLocation);
        GLHelper.checkGlError("glEnableVertexAttribArray");

        GLES20.glVertexAttribPointer(aPositionLocation, 3,
                GLES20.GL_FLOAT, false, sphere.getVerticesStride(), sphere.getVertices());
        GLHelper.checkGlError("glVertexAttribPointer");

        GLES20.glEnableVertexAttribArray(aTextureCoordLocation);
        GLHelper.checkGlError("glEnableVertexAttribArray");

        GLES20.glVertexAttribPointer(aTextureCoordLocation, 2,
                GLES20.GL_FLOAT, false, sphere.getVerticesStride(),
                sphere.getVertices().duplicate().position(3));
        GLHelper.checkGlError("glVertexAttribPointer");

        if (mPendingCameraUpdate) {
            updateViewMatrix();
            mPendingCameraUpdate = false;
        }

        Matrix.translateM(textureMatrix, 0, 0, 1, 0);

        Matrix.multiplyMM(mPvMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
        Matrix.multiplyMM(mMvpMatrix, 0, mPvMatrix, 0, mModelMatrix , 0);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);

        GLES20.glUniformMatrix4fv(uTextureMatrixLocation, 1, false, textureMatrix, 0);
        GLES20.glUniformMatrix4fv(uMVPMatrixLocation, 1, false, mMvpMatrix, 0);

        for (int j = 0; j < sphere.getNumIndices().length; ++j) {
            GLES20.glDrawElements(GLES20.GL_TRIANGLES,
                    sphere.getNumIndices()[j], GLES20.GL_UNSIGNED_SHORT,
                    sphere.getIndices()[j]);
        }

        GLES20.glDisableVertexAttribArray(aPositionLocation);
        GLES20.glDisableVertexAttribArray(aTextureCoordLocation);
    }

    @Override
    public void releaseGLProgram() {
        mShaderProgram.release();
        mInited = false;
    }

    // display has changed, update projectMatrix
    @Override
    public void updateViewport(int width, int height) {
        float aspectRatio = (float) width / height;
        Matrix.perspectiveM(mProjectionMatrix, 0, FOVY, aspectRatio, Z_NEAR, Z_FAR);
        Matrix.setIdentityM(mViewMatrix, 0);
        Matrix.setIdentityM(mModelMatrix, 0);
    }

    @Override
    public void updateCamera(float deltaX, float deltaY) {
        lon = deltaY * DRAG_FRICTION + lon;
        lat = deltaX * DRAG_FRICTION + lat;
        mPendingCameraUpdate = true;
    }

    private void updateViewMatrix() {

        float theta = (float) Math.toRadians(lon);
        float phi = (float) Math.toRadians(lat);

        mCamera[0] = (float)(SPHERE_RADIUS * Math.sin(theta) * Math.cos(phi));
        mCamera[1] = (float)(SPHERE_RADIUS * Math.sin(theta) * Math.sin(phi));
        mCamera[2] = (float)(SPHERE_RADIUS * Math.cos(theta));

        Matrix.setLookAtM(mViewMatrix, 0,
                0, 0, 0,
                mCamera[0], mCamera[1], mCamera[2],
                0, 0, 1);
    }
}


